﻿#region Header
// ------------------------ Licence / Copyright ------------------------
// 
// ScrumTable for Scrum Meeting Support
// Copyright © HSR - Hochschule für Technik Rapperswil
// All Rights Reserved
// 
// Author:
//  Michael Gfeller Silvan Gehrig Patrick Boos
// 
// ---------------------------------------------------------------------
#endregion

#region Usings

using System;
using System.Collections.Generic;
using System.Linq;
using ScrumTable.Common;
using ScrumTable.Common.Collections;
using ScrumTable.DL.Data.Common.Properties;

#endregion

namespace ScrumTable.DL.Data.Common.Serialization
{
    /// <summary>
    /// Represents an index for the DataObject elements in a DataStorage.
    /// </summary>
    public class DataObjectIndex<T> : IEnumerable<DataObjectIndexEntry<T>>
    {
        #region Declarations
        //--------------------------------------------------------------------
        // Declarations
        //--------------------------------------------------------------------

        private readonly IList<string> _schemaList;
        private readonly IDictionary<ValueChain<string>, DataObjectIndexEntry<T>> _indexByParentIdChain;
        private readonly IDictionary<string, IList<DataObjectIndexEntry<T>>> _indexBySchemaAndId;

        #endregion

        #region Properties
        //--------------------------------------------------------------------
        // Properties
        //--------------------------------------------------------------------

        /// <summary>
        /// Gets the index entry which is associated to the given id
        /// key chain of a raw object (an object to deserialize).
        /// </summary>
        /// <param name="rawIds">Specifies the raw id chain of the object.</param>
        /// <returns>Returns the retrieved index entry.</returns>
        public DataObjectIndexEntry<T> this[ValueChain<string> rawIds]
        {
            get
            {
                if (!_indexByParentIdChain.ContainsKey(rawIds))
                    throw new InvalidOperationException(string.Format(Resources.ExcInvalidKeyChainSpecified, rawIds.Chain));

                return _indexByParentIdChain[rawIds];
            }
        }

        #endregion

        #region Constructors / Destructor
        //--------------------------------------------------------------------
        // Constructors / Destructor
        //--------------------------------------------------------------------

        /// <summary>
        /// Creates a new DataObjectIndex instance and indexies the elements
        /// in the given storage.
        /// </summary>
        /// <param name="storage">Provides the storage to index.</param>
        public DataObjectIndex(DataStorage storage)
        {
            PreCondition.AssertNotNull(storage, "storage");

            int indexPtr = 0;
            _indexByParentIdChain = new Dictionary<ValueChain<string>, DataObjectIndexEntry<T>>();
            _schemaList = new List<string>();
            _indexBySchemaAndId = new Dictionary<string, IList<DataObjectIndexEntry<T>>>();

            // create indecis
            foreach (DataAccessObject dataAccessObject in storage.DataAccessObjects)
            {
                _schemaList.Add(dataAccessObject.Schema);
                _indexBySchemaAndId[dataAccessObject.Schema] = new List<DataObjectIndexEntry<T>>();

                var dataObjects = from dataObject in dataAccessObject.DataObjects
                                  orderby dataObject.OrderIndex, dataObject.Id
                                  select dataObject;

                foreach (DataObject dataObject in dataObjects)
                {
                    ValueChain<string> parentIds = ValueChainSerializer.Parse(dataObject.ParentIds);
                    ValueChain<string> objectIds = new ValueChain<string>(parentIds, dataObject.Id);
                    DataObjectIndexEntry<T> indexEntry = new DataObjectIndexEntry<T>(
                        ++indexPtr,
                        dataAccessObject,
                        parentIds,
                        dataObject,
                        objectIds);

                    _indexByParentIdChain.Add(objectIds, indexEntry); // index by parent key chain
                    _indexBySchemaAndId[dataAccessObject.Schema].Add(indexEntry); // index by schema and id
                }
            }
        }

        #endregion

        #region Methods
        //--------------------------------------------------------------------
        // Methods
        //--------------------------------------------------------------------

        /// <summary>
        /// Gets an iterator which can be used to loop through the elements
        /// of a specific dao.
        /// </summary>
        /// <param name="schemaName">Specifies the name of the schema.</param>
        /// <returns>Returns the created iterator.</returns>
        public IEnumerable<DataObjectIndexEntry<T>> GetByDataAccessObject(string schemaName)
        {
            return GetByDataAccessObjects(schemaName);
        }

        /// <summary>
        /// Gets an iterator which can be used to loop through the elements
        /// of a specific dao.
        /// </summary>
        /// <param name="schemaNames">Specifies the names of the schema to search for objects.</param>
        /// <returns>Returns the created iterator.</returns>
        public IEnumerable<DataObjectIndexEntry<T>> GetByDataAccessObjects(params string[] schemaNames)
        {
            return GetEntriesBySchemata(schemaNames);
        }

        /// <summary>
        /// Returns an enumerator that iterates through a collection.
        /// </summary>
        /// <returns>
        /// An <see cref="T:System.Collections.IEnumerator"/> object that can be used to iterate through the collection.
        /// </returns>
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return ((IEnumerable<DataObjectIndexEntry<T>>)this).GetEnumerator();
        }

        /// <summary>
        /// Returns an enumerator that iterates through the collection.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.Collections.Generic.IEnumerator`1"/> that can be used to iterate through the collection.
        /// </returns>
        public IEnumerator<DataObjectIndexEntry<T>> GetEnumerator()
        {
            return GetEntriesBySchemata(_schemaList).GetEnumerator();
        }

        private IEnumerable<DataObjectIndexEntry<T>> GetEntriesBySchemata(IEnumerable<string> schemaNames)
        {
            foreach (string schemaName in schemaNames)
            {
                if (!_indexBySchemaAndId.ContainsKey(schemaName))
                    continue;

                foreach (var dataIndexEntry in _indexBySchemaAndId[schemaName])
                {
                    yield return dataIndexEntry;
                }
            }
        }

        #endregion

        #region Events
        //--------------------------------------------------------------------
        // Events
        //--------------------------------------------------------------------

        #endregion
    }
}